/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib.tests;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

import ac.jp.u_tokyo.SyncLib.Mod;
import ac.jp.u_tokyo.SyncLib.NullMod;
import ac.jp.u_tokyo.SyncLib.PrimMod;
import ac.jp.u_tokyo.SyncLib.SynchronizationFailedException;
import ac.jp.u_tokyo.SyncLib.dictionaries.DictMod;
import ac.jp.u_tokyo.SyncLib.dictionaries.DictModFactory;

public abstract class GetTestBase extends SyncTestBase
{

	protected static final String KEY = "testkey";

	private PrimMod<Integer> _primMod1 = new PrimMod<Integer>(100);

	private PrimMod<Integer> _primMod2 = new PrimMod<Integer>(105);

	private DictMod<String, Integer> createDictMod1(final String key2)
	{
		DictModFactory<String, Integer> k = new DictModFactory<String, Integer>();
		k.addPut(key2, _primMod1);
		DictMod<String, Integer> dictmod = k.create();
		return dictmod;
	}

	private DictMod<String, Integer> createDictMod2(final String key2)
	{
		DictModFactory<String, Integer> k = new DictModFactory<String, Integer>();
		k.addPut(key2, _primMod2);
		DictMod<String, Integer> dictmod = k.create();
		return dictmod;
	}

	protected abstract Mod[] internalSynchronize(Mod dictMod1,
			Mod<Integer> mod1) throws SynchronizationFailedException;

	protected final Mod[] synchronize(Mod dictMod1,
			Mod<Integer> mod1) throws SynchronizationFailedException{
		Mod[] result = internalSynchronize(dictMod1, mod1);
		assertPreservation(dictMod1, mod1, result);
		return result;
	}

	private void assertPreservation(Mod dictMod1,
			Mod<Integer> mod1, Mod[] result)
	{
		super.assertPreservation(result,new Mod[] {dictMod1, mod1});
	}

	protected abstract Mod[] internalResynchronize(Map<String, Integer> map,
			Integer v, Mod dictMod, Mod<Integer> mod)
			throws SynchronizationFailedException;

	protected final Mod[] resynchronize(Map<String, Integer> map,
			Integer v, Mod dictMod, Mod<Integer> mod)
			throws SynchronizationFailedException{
		Mod[] result = internalResynchronize(map, v, dictMod, mod);
		assertPreservation(dictMod, mod, result);
		return result;
	}

	protected abstract void setDictOverVal(boolean dictOverVal);

	@Test
	public void testNull() throws SynchronizationFailedException
	{
		final Mod[] nulls = new Mod[] {NullMod.INSTANCE, NullMod.INSTANCE};
		final Mod[] result = synchronize(NullMod.INSTANCE, NullMod.INSTANCE);
		assertArrayEquals(nulls, result);
	}

	@Test
	public void test1to2() throws SynchronizationFailedException
	{
		DictMod<String, Integer> dictmod = createDictMod1(KEY);
		final Mod[] result = synchronize(dictmod, NullMod.INSTANCE);
		assertEquals(dictmod, result[0]);
		assertEquals(_primMod1, result[1]);
	}

	@Test(expected = SynchronizationFailedException.class)
	public void testConflict1and2() throws SynchronizationFailedException
	{
		DictMod<String, Integer> dictmod1 = createDictMod1(KEY);
		synchronize(dictmod1, _primMod2);
	}

	@Test(expected = SynchronizationFailedException.class)
	public void testDict2Null() throws SynchronizationFailedException
	{
		synchronize(PrimMod.NULL, NullMod.INSTANCE);
	}
	
	@Test
	public void testResynchronize1to2() throws SynchronizationFailedException
	{
		Map<String, Integer> map1 = new HashMap<String, Integer>();
		map1.put(KEY, _primMod1.getNewValue());
		final Mod[] result = resynchronize(map1, null, NullMod.INSTANCE, NullMod.INSTANCE);

		assertEquals(createDictMod1(KEY), result[0]);
		assertEquals(_primMod1, result[1]);
	}

	@Test
	public void testResynchronize2to1() throws SynchronizationFailedException
	{
		Map<String, Integer> map1 = new HashMap<String, Integer>();
		map1.put(KEY, _primMod1.getNewValue());
		setDictOverVal(false);
		final Mod[] result = resynchronize(map1, null, NullMod.INSTANCE, NullMod.INSTANCE);

		DictModFactory<String, Integer> k = new DictModFactory<String, Integer>();
		k.addRemove(KEY);
		assertEquals(k.create(), result[0]);
		assertEquals(new PrimMod(null), result[1]);
	}

	@Test
	public void testModOveridden() throws SynchronizationFailedException
	{
		Map<String, Integer> map1 = new HashMap<String, Integer>();
		map1.put(KEY, _primMod1.getNewValue());
		final Mod[] result = resynchronize(map1, null, NullMod.INSTANCE,
				_primMod2);

		assertEquals(createDictMod2(KEY), result[0]);
		assertEquals(_primMod2, result[1]);
	}
}
